home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-19 / rcs55.zip / RCSDIFF.C < prev    next >
C/C++ Source or Header  |  1991-09-15  |  11KB  |  399 lines

  1. /*
  2.  *                     RCS rcsdiff operation
  3.  */
  4. /*****************************************************************************
  5.  *                       generate difference between RCS revisions
  6.  *****************************************************************************
  7.  */
  8.  
  9. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  10.    Copyright 1990 by Paul Eggert
  11.    Distributed under license by the Free Software Foundation, Inc.
  12.  
  13. This file is part of RCS.
  14.  
  15. RCS is free software; you can redistribute it and/or modify
  16. it under the terms of the GNU General Public License as published by
  17. the Free Software Foundation; either version 1, or (at your option)
  18. any later version.
  19.  
  20. RCS is distributed in the hope that it will be useful,
  21. but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23. GNU General Public License for more details.
  24.  
  25. You should have received a copy of the GNU General Public License
  26. along with RCS; see the file COPYING.  If not, write to
  27. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  28.  
  29. Report problems and direct all questions to:
  30.  
  31.     rcs-bugs@cs.purdue.edu
  32.  
  33. */
  34.  
  35.  
  36.  
  37.  
  38. /* $Log: rcsdiff.c%v $
  39.  * Revision 1.2  1991/08/23  13:32:43  SGP
  40.  * Ported to MSDOS using Borland C++
  41.  *
  42.  * Revision 5.7  1990/12/13  06:54:07  eggert
  43.  * GNU diff 1.15 has -u.
  44.  *
  45.  * Revision 5.6  1990/11/01  05:03:39  eggert
  46.  * Remove unneeded setid check.
  47.  *
  48.  * Revision 5.5  1990/10/04  06:30:19  eggert
  49.  * Accumulate exit status across files.
  50.  *
  51.  * Revision 5.4  1990/09/27  01:31:43  eggert
  52.  * Yield 1, not EXIT_FAILURE, when diffs are found.
  53.  *
  54.  * Revision 5.3  1990/09/11  02:41:11  eggert
  55.  * Simplify -kkvl test.
  56.  *
  57.  * Revision 5.2  1990/09/04  17:07:19  eggert
  58.  * Diff's argv was too small by 1.
  59.  *
  60.  * Revision 5.1  1990/08/29  07:13:55  eggert
  61.  * Add -kkvl.
  62.  *
  63.  * Revision 5.0  1990/08/22  08:12:46  eggert
  64.  * Add -k, -V.  Don't use access().  Add setuid support.
  65.  * Remove compile-time limits; use malloc instead.
  66.  * Don't pass arguments with leading '+' to diff; GNU DIFF treats them as options.
  67.  * Add GNU diff's flags.  Make lock and temp files faster and safer.
  68.  * Ansify and Posixate.
  69.  *
  70.  * Revision 4.6  89/05/01  15:12:27  narten
  71.  * changed copyright header to reflect current distribution rules
  72.  * 
  73.  * Revision 4.5  88/08/09  19:12:41  eggert
  74.  * Use execv(), not system(); yield exit status like diff(1)s; allow cc -R.
  75.  * 
  76.  * Revision 4.4  87/12/18  11:37:46  narten
  77.  * changes Jay Lepreau made in the 4.3 BSD version, to add support for
  78.  * "-i", "-w", and "-t" flags and to permit flags to be bundled together,
  79.  * merged in.
  80.  * 
  81.  * Revision 4.3  87/10/18  10:31:42  narten
  82.  * Updating version numbers. Changes relative to 1.1 actually
  83.  * relative to 4.1
  84.  * 
  85.  * Revision 1.3  87/09/24  13:59:21  narten
  86.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  87.  * warnings)
  88.  * 
  89.  * Revision 1.2  87/03/27  14:22:15  jenkins
  90.  * Port to suns
  91.  * 
  92.  * Revision 4.1  83/05/03  22:13:19  wft
  93.  * Added default branch, option -q, exit status like diff.
  94.  * Added fterror() to replace faterror().
  95.  * 
  96.  * Revision 3.6  83/01/15  17:52:40  wft
  97.  * Expanded mainprogram to handle multiple RCS files.
  98.  *
  99.  * Revision 3.5  83/01/06  09:33:45  wft
  100.  * Fixed passing of -c (context) option to diff.
  101.  *
  102.  * Revision 3.4  82/12/24  15:28:38  wft
  103.  * Added call to catchsig().
  104.  *
  105.  * Revision 3.3  82/12/10  16:08:17  wft
  106.  * Corrected checking of return code from diff; improved error msgs.
  107.  *
  108.  * Revision 3.2  82/12/04  13:20:09  wft
  109.  * replaced getdelta() with gettree(). Changed diagnostics.
  110.  *
  111.  * Revision 3.1  82/11/28  19:25:04  wft
  112.  * Initial revision.
  113.  *
  114.  */
  115. #include "rcsbase.h"
  116.  
  117. #if DIFF_L
  118. static const char *setup_label P((struct buf*,const char*,const char[datesize]));
  119. #endif
  120. static void cleanup P((void));
  121.  
  122. static const char co[] = CO;
  123.  
  124. static int exitstatus;
  125.  
  126. mainProg(rcsdiffId, "rcsdiff", "$Id: rcsdiff.c%v 1.2 1991/08/23 13:32:43 SGP Exp $")
  127. {
  128.     static const char cmdusage[] =
  129.         "\nrcsdiff usage: rcsdiff [-q] [-rrev1 [-rrev2]] [-Vn] [diff options] file ...";
  130.     static const char quietarg[] = "-q";
  131.  
  132.     int  revnums;                 /* counter for revision numbers given */
  133.     const char *rev1, *rev2;    /* revision numbers from command line */
  134.     const char *xrev1, *xrev2;    /* expanded revision numbers */
  135.     const char *expandarg, *lexpandarg, *versionarg;
  136. #if DIFF_L
  137.     static struct buf labelbuf[2];
  138.     int file_labels;
  139.     const char **diff_label1, **diff_label2;
  140.     char date2[datesize];
  141. #endif
  142.     const char **diffv, **diffp;    /* argv for subsidiary diff */
  143.     const char **pp, *p, *diffvstr;
  144.     struct buf commarg;
  145.     struct buf numericrev;    /* expanded revision number */
  146.     struct hshentries *gendeltas;    /* deltas to be generated */
  147.     struct hshentry * target;
  148.     int  exit_stats;
  149.     char *argp, *dcp;
  150.     register c;
  151.  
  152.     initid();
  153.     catchints();
  154.  
  155.     bufautobegin(&commarg);
  156.     bufautobegin(&numericrev);
  157.     revnums = 0;
  158.     rev1 = rev2 = xrev2 = nil;
  159. #if DIFF_L
  160.     file_labels = 0;
  161. #endif
  162.     expandarg = versionarg = quietarg; /* i.e. a no-op */
  163.  
  164.     /* Room for args + 2 i/o [+ 2 labels] + 1 file + 1 trailing null.  */
  165.     diffp = diffv = tnalloc(const char*, argc + 4 + 2*DIFF_L);
  166.     *diffp++ = nil;
  167.     *diffp++ = nil;
  168.     *diffp++ = DIFF;
  169.  
  170.     while (--argc,++argv, argc>=1 && ((*argv)[0] == '-')) {
  171.     dcp = argp = *argv + 1;
  172.     while (c = *argp++) switch (c) {
  173.         case 'r':
  174.             if (*argp!='\0') {
  175.             if (revnums==0) {
  176.                 rev1= argp; revnums=1;
  177.             } else if (revnums==1) {
  178.                 rev2= argp; revnums=2;
  179.             } else {
  180.                 faterror("too many revision numbers");
  181.             }
  182.             } /* do nothing for empty -r */
  183.             goto option_handled;
  184. #if DIFF_L
  185.         case 'L':
  186.             if (++file_labels == 2)
  187.             faterror("too many -L options");
  188.             /* fall into */
  189. #endif
  190.         case 'C': case 'D': case 'F': case 'I':
  191.             *dcp++ = c;
  192.             if (*argp)
  193.             do *dcp++ = *argp;
  194.             while (*++argp);
  195.             else {
  196.             if (!--argc)
  197.                 faterror("-%c needs following argument%s",
  198.                     c, cmdusage
  199.                 );
  200.             *diffp++ = *argv++;
  201.             }
  202.             break;
  203.         case 'B': case 'H': case 'T':
  204.         case '0': case '1': case '2': case '3': case '4':
  205.         case '5': case '6': case '7': case '8': case '9':
  206.         case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
  207.         case 'h': case 'i': case 'n': case 'p':
  208.         case 't': case 'u': case 'w':
  209.             *dcp++ = c;
  210.             break;
  211.         case 'q':
  212.             quietflag=true;
  213.             break;
  214.         case 'V':
  215.             versionarg = *argv;
  216.             setRCSversion(versionarg);
  217.             goto option_handled;
  218.         case 'k':
  219.             expandarg = *argv;
  220.             if (0 <= str2expmode(expandarg+2))
  221.             goto option_handled;
  222.             /* fall into */
  223.         default:
  224.             faterror("unknown option: %s%s", *argv, cmdusage);
  225.         };
  226.       option_handled:
  227.     if (dcp != *argv+1) {
  228.         *dcp = 0;
  229.         *diffp++ = *argv;
  230.     }
  231.     } /* end of option processing */
  232.  
  233.     if (argc<1) faterror("no input file%s", cmdusage);
  234.  
  235.     for (pp = diffv+3, c = 0;  pp<diffp;  )
  236.         c += strlen(*pp++) + 1;
  237.     diffvstr = argp = tnalloc(char, c + 1);
  238.     for (pp = diffv+3;  pp<diffp;  ) {
  239.         p = *pp++;
  240.         *argp++ = ' ';
  241.         while ((*argp = *p++))
  242.             argp++;
  243.     }
  244.     *argp = 0;
  245.  
  246. #if DIFF_L
  247.     diff_label1 = diff_label2 = nil;
  248.     if (file_labels < 2) {
  249.         if (!file_labels)
  250.             diff_label1 = diffp++;
  251.         diff_label2 = diffp++;
  252.     }
  253. #endif
  254.     diffp[2] = nil;
  255.  
  256.     /* now handle all filenames */
  257.     do {
  258.         finptr=NULL;
  259.         ffree();
  260.  
  261.         if (pairfilenames(argc, argv, rcsreadopen, true, false) != 1)
  262.             continue;
  263.         diagnose("===================================================================\nRCS file: %s\n",RCSfilename);
  264.         if (!rev2) {
  265.         /* Make sure work file is readable, and get its status.  */
  266.         if ((c = open(workfilename,O_RDONLY,0)) < 0) {
  267.             eerror(workfilename);
  268.             continue;
  269.         }
  270.         if (!getfworkstat(c)) continue;
  271.         VOID close(c);
  272.         }
  273.  
  274.  
  275.         gettree(); /* reads in the delta tree */
  276.  
  277.         if (Head==nil) {
  278.             error("no revisions present");
  279.             continue;
  280.         }
  281.         if (revnums==0)
  282.             rev1  =  Dbranch ? Dbranch : Head->num;
  283.  
  284.         if (!expandsym(rev1,&numericrev)) continue;
  285.         if (!(target=genrevs(numericrev.string,(char *)nil,(char *)nil,(char *)nil,&gendeltas))) continue;
  286.         xrev1=target->num;
  287. #if DIFF_L
  288.         if (diff_label1)
  289.         *diff_label1 = setup_label(&labelbuf[0], target->num, target->date);
  290. #endif
  291.  
  292.         lexpandarg = expandarg;
  293.         if (revnums==2) {
  294.             if (!expandsym(rev2, &numericrev)) continue;
  295.             if (!(target=genrevs(numericrev.string,(char *)nil,(char *)nil,(char *)nil,&gendeltas))) continue;
  296.             xrev2=target->num;
  297.         } else if (
  298.             target->lockedby
  299.         &&    lexpandarg == quietarg
  300.         &&    Expand == KEYVAL_EXPAND
  301.         &&    WORKMODE(RCSstat.st_mode,true) == workstat.st_mode
  302.         )
  303.             lexpandarg = "-kkvl";
  304. #if DIFF_L
  305.         if (diff_label2)
  306.         if (revnums == 2)
  307.             *diff_label2 = setup_label(&labelbuf[1], target->num, target->date);
  308.         else {
  309.             time2date(workstat.st_mtime, date2);
  310.             *diff_label2 = setup_label(&labelbuf[1], workfilename, date2);
  311.         }
  312. #endif
  313.  
  314.         diffp[0] = maketemp(0);
  315.         diagnose("retrieving revision %s\n", xrev1);
  316.         bufscpy(&commarg, "-p");
  317.         bufscat(&commarg, xrev1);
  318.         if (run((char*)nil,diffp[0], co,quietarg,commarg.string,lexpandarg,versionarg,RCSfilename,(char*)nil)){
  319.             error("co failed");
  320.             continue;
  321.         }
  322.         if (!rev2) {
  323.             diffp[1] = workfilename;
  324.             if (workfilename[0] == '+') {
  325.                 /* Some diffs have options with leading '+'. */
  326.                 diffp[1] = argp =
  327.                     ftnalloc(char, strlen(workfilename)+3);
  328.                 *argp++ = '.';
  329.                 *argp++ = SLASH;
  330.                 VOID strcpy(argp, workfilename);
  331.             }
  332.         } else {
  333.             diffp[1] = maketemp(1);
  334.             diagnose("retrieving revision %s\n",xrev2);
  335.             bufscpy(&commarg, "-p");
  336.             bufscat(&commarg, xrev2);
  337.             if (run((char*)nil,diffp[1], co,quietarg,commarg.string,expandarg,versionarg,RCSfilename,(char *)nil)){
  338.                 error("co failed");
  339.                 continue;
  340.             }
  341.         }
  342.         if (!rev2)
  343.             diagnose("diff%s -r%s %s\n", diffvstr, xrev1, workfilename);
  344.         else
  345.             diagnose("diff%s -r%s -r%s\n", diffvstr, xrev1, xrev2);
  346.  
  347.         exit_stats = runv(diffv);
  348.  
  349.         if (exit_stats)
  350.             if (WIFEXITED(exit_stats) && WEXITSTATUS(exit_stats)==1) {
  351.                 if (!exitstatus)
  352.                     exitstatus = 1;
  353.             } else
  354.                 error("diff failed");
  355.     } while (cleanup(),
  356.          ++argv, --argc >=1);
  357.  
  358.  
  359.     tempunlink();
  360.     exitmain(exitstatus);
  361. }
  362.  
  363.     static void
  364. cleanup()
  365. {
  366.     if (nerror) exitstatus = EXIT_TROUBLE;
  367.     if (finptr) ffclose(finptr);
  368. }
  369.  
  370. #if lint
  371. #    define exiterr rdiffExit
  372. #endif
  373.     exiting void
  374. exiterr()
  375. {
  376.     tempunlink();
  377.     _exit(EXIT_TROUBLE);
  378. }
  379.  
  380. #if DIFF_L
  381.     static const char *
  382. setup_label(struct buf *b,
  383.             const char *name,
  384.             const char date[datesize])
  385. {
  386.     const char *p;
  387.  
  388.     bufalloc(b,  2+strlen(name)+1+datesize);
  389.     for (p = date;  *p++ != '.';  )
  390.         ;
  391.     VOID sprintf(b->string, "-L%s\t%s%.*s/%.2s/%.2s %.2s:%.2s:%s",
  392.         name,
  393.         date[2]=='.' ? "19" : "",
  394.         p-date-1, date, p, p+3, p+6, p+9, p+12
  395.     );
  396.     return b->string;
  397. }
  398. #endif
  399.